import { readonly, ref } from 'vue';

import { DIGITS, OPERATORS } from '../shared/constants';

export function useCalculate() {
  const memory = ref('');
  const error = ref(false);
  const clearOnNextDigit = ref(false);

  function isOperator(string: string) {
    return OPERATORS.includes(string);
  }

  function isDigit(string: string) {
    return DIGITS.includes(string);
  }

  function lastCharIsOperator(string: string) {
    const stringNormalized = string.replaceAll(/\s/g, '');
    return isOperator(stringNormalized[stringNormalized.length - 1] as string);
  }

  function addDigit(digit: string) {
    if (!isDigit(digit)) {
      throw new Error('Invalid param, is not a valid digit');
    }

    const lastDigit = memory.value[memory.value.length - 1];

    if (lastDigit === '.' && digit === '.') return;
    if (lastDigit === '0' && memory.value.length === 1) clear();
    if (clearOnNextDigit.value) clear();
    if ((!memory.value || lastCharIsOperator(memory.value)) && digit === '.')
      memory.value += '0';

    clearOnNextDigit.value = false;
    memory.value += `${digit}`;
  }

  function addOperator(operator: string) {
    if (!isOperator(operator)) {
      throw new Error('Invalid param, is not a valid operator');
    }

    if (!memory.value && operator !== '-') return;
    if (lastCharIsOperator(memory.value)) eraseLast();

    clearOnNextDigit.value = false;
    memory.value += `${operator}`;
  }

  function calculateResult() {
    if (!memory.value) return;

    if (lastCharIsOperator(memory.value)) {
      memory.value = memory.value.slice(0, -1);
    }
    try {
      const mathExpression = memory.value.replaceAll(
        // eslint-disable-next-line regexp/no-super-linear-backtracking
        /\b0*((\d+\.\d+|\d+))\b/g,
        '$1',
      ); // remove octal numeric
      const result = mathExpression.replaceAll(
        /(\d+)\s*([*+/\\-])\s*(\d+)/g,
        (_substring: string, a: string, op: string, b: string): string => {
          switch (op) {
            case '*': {
              return (Number(a) * Number(b)).toString();
            }
            case '+': {
              return (Number(a) + Number(b)).toString();
            }
            case '/': {
              return (Number(a) / Number(b)).toString();
            }
            case '-': {
              return (Number(a) - Number(b)).toString();
            }
          }
          return '';
        },
      );
      memory.value = result;
    } catch {
      error.value = true;
      memory.value = '';
    } finally {
      clearOnNextDigit.value = true;
    }
  }

  function eraseLast() {
    if (memory.value.length === 0) return;

    memory.value = memory.value.slice(0, -1);
    clearOnNextDigit.value = false;
  }

  function clear() {
    memory.value = '';
    error.value = false;
  }

  return {
    addDigit,
    addOperator,
    calculateResult,
    clear,
    eraseLast,
    error: readonly(error),
    memory: readonly(memory),
  };
}
